#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Cross-platform bootstrap script for deployment.
Supports Windows, Linux, and macOS.
Handles Windows-specific executable names like mvn.cmd.
Includes deleting the namespace after Helm uninstall.
Adds --wait and --timeout to helm install/upgrade.
"""

import argparse
import os
import sys
import subprocess
import time
from pathlib import Path
import platform

def parse_arguments():
    """1. Check arguments"""
    parser = argparse.ArgumentParser(description="Bootstrap deployment script.")
    parser.add_argument('-e', '--env', required=True, help='Environment (e.g., dev, prod)')
    parser.add_argument('-n', '--name', required=True, help='Deployment name')
    parser.add_argument('-r', '--root', help='Root directory path (default: current directory)')
    parser.add_argument('-t', '--timeout', default='5m', help='Timeout for Helm operations (e.g., 300s, 5m). Default: 5m')

    args = parser.parse_args()

    # 1.1 Check if env is not empty
    if not args.env.strip():
        print("Error: Environment (-e, --env) cannot be empty.", file=sys.stderr)
        sys.exit(1)

    # 1.2 Check if name is not empty
    if not args.name.strip():
        print("Error: Name (-n, --name) cannot be empty.", file=sys.stderr)
        sys.exit(1)

    # 1.3 If root is provided, check if the directory exists
    if args.root:
        root_path = Path(args.root)
        if not root_path.is_dir():
            print(f"Error: Root directory '{args.root}' does not exist or is not a directory.", file=sys.stderr)
            sys.exit(1)
        # 1.4 If root is valid, change to that directory
        try:
            os.chdir(root_path)
            print(f"Changed working directory to: {os.getcwd()}")
        except Exception as e:
            print(f"Error: Could not change to root directory '{args.root}'. {e}", file=sys.stderr)
            sys.exit(1)

    return args

def get_executable_name(tool_name):
    """
    Returns the appropriate executable name for the current platform.
    E.g., 'mvn' on Linux/macOS, 'mvn.cmd' on Windows.
    """
    system = platform.system().lower()
    if system == 'windows' and tool_name == 'mvn':
        return 'mvn.cmd'
    return tool_name

def check_tool_version(tool_name, version_args):
    """Helper to check version for a specific tool with its specific args."""
    executable = get_executable_name(tool_name)
    try:
        result = subprocess.run([executable] + version_args, capture_output=True, text=True, check=True)
        # Some tools print version to stderr (e.g., java)
        version_output = result.stdout.strip() if result.stdout.strip() else result.stderr.strip()
        # Sometimes version info is on a long first line, take the first part
        version_line = version_output.split('\n')[0]
        print(f"{tool_name} version: {version_line}")
    except subprocess.CalledProcessError as e:
        print(f"Error: Failed to get version for '{tool_name}' using '{' '.join([executable] + version_args)}'. Command exited with code {e.returncode}.", file=sys.stderr)
        # Print stderr for more context on the error
        if e.stderr:
             print(f"STDERR: {e.stderr.strip()}", file=sys.stderr)
        sys.exit(1)
    except FileNotFoundError:
        print(f"Error: '{executable}' (for '{tool_name}') is not found in the system PATH.", file=sys.stderr)
        sys.exit(1)


def check_environment():
    """2. Check environment tools and print versions"""
    # Define tools and their specific version arguments
    # Note: We use the base tool name here, get_executable_name will resolve it
    tools_and_args = [
        ('java', ['-version']),
        ('mvn', ['-v']),
        ('docker', ['--version']),
        ('kubectl', ['version', '--client=true']), # Use --client to avoid server connection
        ('helm', ['version', '--short']) # --short for a concise version
    ]

    for tool_name, version_args in tools_and_args:
        check_tool_version(tool_name, version_args)


def set_variables(name, env):
    """3. Set variables and print them"""
    # 3.1 namespace=<name>-<env>
    namespace = f"{name}-{env}"
    print(f"namespace={namespace}")

    # 3.2 mode: Check if namespace exists
    try:
        # Run kubectl get namespace and capture output
        # Using check_output to easily catch if the namespace is not found
        subprocess.check_output(
            ['kubectl', 'get', 'namespace', namespace],
            stderr=subprocess.STDOUT, text=True
        )
        # If the command succeeds, the namespace exists
        mode = "UNINSTALL"
    except subprocess.CalledProcessError as e:
        # If the command fails, check if it's because the namespace was not found
        # A typical error message contains 'NotFound' or 'not found'
        if "not found" in e.output.lower() or "notfound" in e.output.lower():
            mode = "INSTALL"
        else:
            # Some other error occurred
            print(f"Error: Failed to check namespace '{namespace}'. {e.output.strip()}", file=sys.stderr)
            sys.exit(1)
    print(f"mode={mode}")

    # 3.3 img=<name>-app:<env>
    img = f"{name}-app:{env}"
    print(f"img={img}")

    return namespace, mode, img

def execute_deployment(mode, name, namespace, img, env, timeout):
    """5. Execute deployment steps"""
    start_time = time.time()
    try:
        if mode == "INSTALL":
            print("Starting INSTALL process...")
            # 5.1.1 mvn clean package
            mvn_exec = get_executable_name('mvn')
            print(f"Running: {mvn_exec} clean package -DskipTests -Dfile.encoding=UTF-8")
            subprocess.run([mvn_exec, 'clean', 'package', '-DskipTests', '-Dfile.encoding=UTF-8'], check=True)

            # 5.1.2 docker build
            print(f"Running: docker build -t {img} .")
            subprocess.run(['docker', 'build', '-t', img, '.'], check=True)

            # 5.1.3 helm upgrade --install
            # Assuming the values file is located at ./helm/<name>/values-<env>.yaml
            chart_path = f"./helm/{name}"
            values_file = f"{chart_path}/values-{env}.yaml"

            print(f"Running: helm upgrade --install {name} {chart_path} "
                  f"--namespace {namespace} --create-namespace "
                  f"--values {values_file} --wait --timeout {timeout}")
            subprocess.run([
                'helm', 'upgrade', '--install', name, chart_path,
                '--namespace', namespace, '--create-namespace',
                '--values', values_file, '--wait', '--timeout', timeout
            ], check=True)
            print("INSTALL process completed successfully.")

        elif mode == "UNINSTALL":
            print("Starting UNINSTALL process...")
            # 5.2.1 helm uninstall
            print(f"Running: helm uninstall {name} --namespace {namespace}")
            subprocess.run(['helm', 'uninstall', name, '--namespace', namespace], check=True)

            # After successful Helm uninstall, delete the namespace
            print(f"Running: kubectl delete namespace {namespace}")
            subprocess.run(['kubectl', 'delete', 'namespace', namespace], check=True)

            print("UNINSTALL process and namespace deletion completed successfully.")

    except subprocess.CalledProcessError as e:
        print(f"Error: A command failed during execution. Exit code: {e.returncode}", file=sys.stderr)
        # Print stdout and stderr if available for debugging
        if e.stdout:
            print(f"STDOUT: {e.stdout}", file=sys.stderr)
        if e.stderr:
            print(f"STDERR: {e.stderr}", file=sys.stderr)
        sys.exit(1)
    finally:
        # 6. End timing and output duration
        end_time = time.time()
        duration_seconds = int(end_time - start_time)
        minutes = duration_seconds // 60
        seconds = duration_seconds % 60
        print(f"Execution time: {minutes:02d}:{seconds:02d}")

def main():
    """Main function to orchestrate the bootstrap process."""
    # Step 1: Parse and check arguments
    args = parse_arguments()

    # Step 2: Check environment
    check_environment()

    # Step 3: Set variables
    namespace, mode, img = set_variables(args.name, args.env)

    # Step 4: Start timing (moved into execute_deployment for precision)

    # Step 5 & 6: Execute and time
    execute_deployment(mode, args.name, namespace, img, args.env, args.timeout)

if __name__ == "__main__":
    main()